package br.ufla.pgcc.oomeasures.measurement;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;

import br.ufla.pgcc.oomeasures.ASTSession;
import br.ufla.pgcc.oomeasures.ItemMeasured;
import br.ufla.pgcc.oomeasures.astvisitors.MethodLinesOfCodeVisitor;
import br.ufla.pgcc.oomeasures.measures.AttributeHidingFactor;
import br.ufla.pgcc.oomeasures.measures.ClassInterfaceSize;
import br.ufla.pgcc.oomeasures.measures.CommentsRatio;
import br.ufla.pgcc.oomeasures.measures.DataAccessMetric;
import br.ufla.pgcc.oomeasures.measures.ImportCoupling;
import br.ufla.pgcc.oomeasures.measures.LackOfCohesionOfMethods;
import br.ufla.pgcc.oomeasures.measures.LinesOfCode;
import br.ufla.pgcc.oomeasures.measures.Measure;
import br.ufla.pgcc.oomeasures.measures.Measure.Granularity;
import br.ufla.pgcc.oomeasures.measures.MethodHidingFactor;
import br.ufla.pgcc.oomeasures.measures.MethodLinesOfCode;
import br.ufla.pgcc.oomeasures.measures.NumberOfAttributes;
import br.ufla.pgcc.oomeasures.measures.NumberOfClasses;
import br.ufla.pgcc.oomeasures.measures.NumberOfComments;
import br.ufla.pgcc.oomeasures.measures.NumberOfFinalMethods;
import br.ufla.pgcc.oomeasures.measures.NumberOfMethods;
import br.ufla.pgcc.oomeasures.measures.NumberOfStaticAttributes;
import br.ufla.pgcc.oomeasures.measures.NumberOfStaticMethods;

public class Measurement {
	private Measure[] measures = {
			new NumberOfClasses(),
			new NumberOfAttributes(),
			new NumberOfMethods(), 
			new ClassInterfaceSize(),
			new LinesOfCode(),
			new MethodLinesOfCode(),
			new DataAccessMetric(),
			new ImportCoupling(),
			new LackOfCohesionOfMethods(),			
			new NumberOfStaticAttributes(),
			new NumberOfStaticMethods(),
			new NumberOfFinalMethods(),
			new CommentsRatio(),
			new NumberOfComments()};
	
	public void execute() throws CoreException {
		final String className = "HelloWorld.java";
		NumberOfMethods nom = new NumberOfMethods();
		NumberOfAttributes noa = new NumberOfAttributes();
		DataAccessMetric dam = new DataAccessMetric();
		LinesOfCode loc =  new LinesOfCode();
		LackOfCohesionOfMethods lcom = new LackOfCohesionOfMethods();
		NumberOfClasses nc = new NumberOfClasses();
		NumberOfStaticAttributes nsa = new NumberOfStaticAttributes();
		NumberOfStaticMethods nsm = new NumberOfStaticMethods();
		CommentsRatio cr = new CommentsRatio();
		NumberOfComments ncl = new NumberOfComments();

		File javaFile = new File("./test/"+className);
		BufferedReader in;
		try {
			in = new BufferedReader(new FileReader(javaFile));

			final StringBuffer buffer = new StringBuffer();
			String line = null;
			while (null != (line = in.readLine())) {
				buffer.append(line).append("\n");
			}

			System.out.println("The class '"+className+"' has: ");
			char[] code = buffer.toString().toCharArray(); 
			nom.measure(code);
			noa.measure(code);
			dam.measure(code);
			loc.measure(code);
		    lcom.measure(code);
			nc.measure(code);
			nsa.measure(code);
			nsm.measure(code);
			cr.measure(code);
			ncl.measure(code);
			
			System.out.println(nom.getName()+": "+nom.getCalculatedValue());
			System.out.println(noa.getName()+": "+noa.getCalculatedValue());
			System.out.println(dam.getName()+": "+dam.getCalculatedValue());
			System.out.println(loc.getName()+": "+loc.getCalculatedValue());	
			System.out.println(lcom.getName()+": "+lcom.getCalculatedValue());
			System.out.println(nc.getName()+":"+nc.getCalculatedValue());
			System.out.println(nsa.getName()+": "+nsa.getCalculatedValue());
			System.out.println(nsm.getName()+": "+nsa.getCalculatedValue());
			System.out.printf(cr.getName()+": "+cr.getCalculatedValue());
			System.out.printf(ncl.getName()+": "+ncl.getCalculatedValue());
			in.close();

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public ItemMeasured execute(IProject project) throws CoreException {
		ItemMeasured root = null;
		ASTSession.getInstance().reset();
		if (project != null && project.isNatureEnabled("org.eclipse.jdt.core.javanature")) {
			IJavaProject javaProject = JavaCore.create(project);
			root = measure(javaProject);
		}
		return root;
	}


	private ItemMeasured measure(IJavaProject javaProject) {
		ItemMeasured root = new ItemMeasured(javaProject.getElementName(), null);
		try {
			for (Measure m : measures) {
				ItemMeasured measureItem = new ItemMeasured(m.getName(), root);
				measureItem.setMeasure(m);
				analysePackages(m, javaProject, measureItem);
				root.addChild(measureItem);
			}
			Measure[] projectMeasures = {new MethodHidingFactor(),
										 new AttributeHidingFactor()};
			
			for (Measure m : projectMeasures) {
				ItemMeasured measureItem = new ItemMeasured(m.getName(), root);
				measureItem.setMeasure(m);
				m.measure(null);
				double value = m.getCalculatedValue();
				measureItem.setValue(value);
				root.addChild(measureItem);
			}
		} catch (CoreException e) {
			e.printStackTrace();
		}
		return root;
	}

	private void analysePackages(Measure measure, IJavaProject project, ItemMeasured root) throws CoreException,
	JavaModelException {

		IPackageFragment[] packages = project.getPackageFragments();
		String packageName = "";

		for (IPackageFragment myPackage : packages) {
			if ((myPackage.getKind() == IPackageFragmentRoot.K_SOURCE) &&
					myPackage.hasChildren()) {

				packageName = myPackage.isDefaultPackage() ? "(default)" : myPackage.getElementName();
				ItemMeasured packageMeasured = new ItemMeasured(packageName, root);
				analyseClasses(measure, myPackage, packageMeasured);
				root.addChild(packageMeasured);
				root.addValue(packageMeasured.getValue());
			}
		}
	}

	private void analyseClasses(Measure measure, IPackageFragment myPackage, ItemMeasured packageMeasured) throws JavaModelException {
		double value = 0d;
		for (ICompilationUnit unit : myPackage.getCompilationUnits()) {
			
			ItemMeasured classItem = new ItemMeasured(unit.getElementName(), packageMeasured);
			measure.measure(unit);
			value = measure.getCalculatedValue();
			classItem.addValue(value);
			
			if (measure.isApplicableGranularity(Granularity.METHOD)) {
				analyseMethods(measure, unit, classItem);
				packageMeasured.addValue(classItem.getValue());
			} else {
				packageMeasured.addValue(value);	
			}
			packageMeasured.addChild(classItem);
		}
	}

	private void analyseMethods(Measure measure, ICompilationUnit myClass, ItemMeasured classItem)
			throws JavaModelException {
		
		String methodName = "";
		for (IType unit : myClass.getAllTypes()) {
			for (IMethod method : unit.getMethods()) {
				methodName = MethodLinesOfCodeVisitor.getMethodNameWithParameters(method);

				ItemMeasured methodItem = new ItemMeasured(methodName, classItem);
				measure.measure(method);
				double value = measure.getCalculatedValue();
				methodItem.setValue(value);
				classItem.addValue(value);				
				classItem.addChild(methodItem);				
			}
		}
	}
}